const Model = require('../models/torrent-model')
const ClientModel = require('../models/client-model')
const SiteModel = require('../models/btsite-model')
const CategoryModel = require('../models/category-model')
const LibraryModel = require('../models/library-model')
const ResponseModel = require('../models/response-model')
const BtClients = require('../utils/btclients/index')
const baseConfig = require('../base-config')

const LibraryService = require('../services/library-service')

const path = require('path')
const fs = require('fs')
const { fileExists } = require('../utils/common')

const logger = require('../utils/console-logger')

const { findMetaInfo } = require('../core/Meta')

const { parseTorrentName } = require('../utils/torrent/torrent-name-parser')
const TorrentInfoParser = require('../utils/torrent/torrent-info-parser')
const torrentsPath = path.join(baseConfig.configPath, 'torrents')

const { download } = require('../utils/http-utils')

// 推送下载
async function pushToClient(model, pushOptions) {
    logger.info('推送下载: ', model, pushOptions)
    
    if (model && model.clientid && (model.address || model.torrentFile)) {
        const { startNow = false, enableSiteFolder = true, enableCategoryFolder = true } = pushOptions

        const clientModel = await ClientModel.getById(model.clientid)
        if (ClientModel) {
            const client = BtClients.getClient(clientModel)
            const setting = await client.getSetting()
            let pushed = false
            if (setting) {

                // 下载器基础目录
                let downloadDir = setting.downloadDir
                // 站点目录
                if (enableSiteFolder && model.siteid) {
                    const site = await SiteModel.getById(model.siteid)
                    if (site && site.folder) {
                        downloadDir = downloadDir.indexOf(path.posix.sep) >= 0 ? path.posix.join(downloadDir, site.folder) : path.win32.join(downloadDir, site.folder)
                    }
                }
                // 分类目录
                if (enableCategoryFolder && model.categoryid) {
                    const category = await CategoryModel.getById(model.categoryid)
                    if (category && category.folder) {
                        downloadDir = downloadDir.indexOf(path.posix.sep) >= 0 ? path.posix.join(downloadDir, category.folder) : path.win32.join(downloadDir, category.folder)
                    }
                }
                if (model.torrentFile) {
                    pushed = await client.addTorrent(path.join(torrentsPath, model.torrentFile), { downloadDir, startNow })
                } else if (model.address) {
                    pushed = await client.addMagnet(model.address, { downloadDir, startNow })
                }
            }
            // console.log('push res: ', pushed)
            if (pushed) {
                return await client.getTorrent(model.hashString)
            }
        }
    }
}

// 成功识别到媒体信息后的处理
// 设置媒体库、设置子路径（媒体库二级目录）
async function recognizedHandel(model, info) {
    // 根据规则自动识别媒体
    const libraryid = await LibraryService.guessLibrary({
        mediaType: info.type,
        genre_ids: info.genre_ids, 
        original_language: info.original_language, 
        origin_country: info.origin_country,
    })
    // 更新信息
    const rawTitle = `${info.title} (${info.year.split('-')[0]})`;
    const updateModel = {
        tmdbid: info.id,
        mediaType: info.type,
        toLink: rawTitle,
    }
    !model.name && (updateModel.name = rawTitle)
    libraryid && (updateModel.libraryid = libraryid)
    Model.updateBy(updateModel, { id: model.id });
}

module.exports = {

    // 查询
    'get::/torrent/list': async function (ctx) {

        const params = ctx.request.query

        // 'category[]': [ '1', '3' ],
        // 'site[]': [ '1', '3' ],
        // 'client[]': '2',
        // status[]: ['linked', 'unlink']
        // linkDir[]: ['yes', 'no']
        const keyword = params.keyword
        const categorys = params['category[]']
        const sites = params['site[]']
        const clients = params['client[]']
        const status = params['status[]']
        const linkDirs = params['linkDir[]']

        const whereBuilder = modelQuery => {
            if (keyword) {
                modelQuery.where(function () {
                    this.where('name', 'like', `%${keyword}%`)
                        .orWhere('btname', 'like', `%${keyword}%`)
                })
            }
            if (categorys) {
                if (typeof categorys === 'string')
                    modelQuery.where('categoryid', categorys)
                else if (typeof categorys === 'object' && Array.isArray(categorys))
                    modelQuery.whereIn('categoryid', categorys)
            }
            if (sites) {
                if (typeof sites === 'string')
                    modelQuery.where('siteid', sites)
                else if (typeof sites === 'object' && Array.isArray(sites))
                    modelQuery.whereIn('siteid', sites)
            }
            if (clients) {
                if (typeof clients === 'string')
                    modelQuery.where('clientid', clients)
                else if (typeof clients === 'object' && Array.isArray(clients))
                    modelQuery.whereIn('clientid', clients)
            }
            if (status) {
                if (typeof status === 'string')
                    modelQuery.where('status', status)
                else if (typeof status === 'object' && Array.isArray(status))
                    modelQuery.whereIn('status', status)
            }
            if (linkDirs) {
                if (typeof linkDirs === 'string' && linkDirs === 'yes')
                    modelQuery.whereNotNull('link_dir', linkDirs)
                else if (typeof linkDirs === 'string' && linkDirs === 'no')
                    modelQuery.whereNull('link_dir', linkDirs)
            }
        }

        const page = await Model.page(Model.buildPaging(params), whereBuilder)

        ctx.response.body = ResponseModel.success(page)
    },
    // 详情
    'get::/torrent/find/:id': async function (ctx) {
        const { id } = ctx.request.params
        const model = await Model.getById(id)
        ctx.response.body = ResponseModel.success(model)
    },
    // 保存
    'post::/torrent/save': async function (ctx) {

        const model = ctx.request.body

        if (!model.address && !model.torrentFile) {
            ctx.response.body = ResponseModel.failure('参数错误')
            return
        }

        let id = model.id
        const startOnAdd = model.startOnAdd
        const pushOptions = {
            startNow: model.startNow,
            enableSiteFolder: model.enableSiteFolder,
            enableCategoryFolder: model.enableCategoryFolder,
        }

        // 无需更新属性
        delete model.id
        // delete model.name // 可识别获取
        delete model.btid
        delete model.btname
        delete model.linkDir
        delete model.hashString
        // 推送属性
        delete model.startOnAdd
        delete model.startNow
        delete model.enableSiteFolder
        delete model.enableCategoryFolder

        // 解析磁力链接或种子文件
        const torrentInfo = {};
        let downLoadName;
        // 有下载地址，判断是否为磁力链接或种子下载链接
        if (model.address) {
            if (model.address.startsWith('magnet:')) {
                // 1)下载地址为磁力链接，则解析磁力链接
                // A. 解析磁力链接
                const parsed = TorrentInfoParser.magnetLinkInfo(model.address);
                if (parsed) {
                    torrentInfo.name = parsed.dn;
                    torrentInfo.hash = parsed.infoHash;
                }
            } else if (model.address.startsWith('http://') || model.address.startsWith('https://')) {
                // 2)下载地址为种子下载链接，则先下载种子文件
                if (!model.torrentFile || !await fileExists(path.join(torrentsPath, model.torrentFile))) {
                    try {
                        // TODO 考虑重命名种子，可能存在同名种子
                        downLoadName = await download(model.address, torrentsPath);
                        if (!downLoadName) {
                            ctx.response.body = ResponseModel.failure('下载种子文件失败')
                            return
                        }
                        if (!downLoadName.endsWith('.torrent')) {
                            // 不是种子文件，删除
                            await fs.delFile(path.join(torrentsPath, downLoadName));
                            ctx.response.body = ResponseModel.failure('下载的文件不是种子')
                            return
                        }
                    } catch (e) {
                        logger.error('下载种子文件失败', e);
                        ctx.response.body = ResponseModel.failure('下载种子文件失败:' + e.message)
                        return
                    }
                }

            }
        }
        // B. 解析种子文件
        const toParseFile = downLoadName || model.torrentFile // downLoadName 表示此次下载的种子文件
        if (toParseFile) {
            const torrentFile = fs.readFileSync(path.join(torrentsPath, toParseFile));
            if (torrentFile) {
                const parsed = TorrentInfoParser.torrentFileInfo(torrentFile);
                if (parsed) {
                    torrentInfo.name = parsed.name;
                }
                const torrentHash = await TorrentInfoParser.torrentFileHash(torrentFile);
                if (torrentHash)
                    torrentInfo.hash = torrentHash;
            }
        }

        if (!torrentInfo.hash) {
            ctx.response.body = ResponseModel.failure('种子解析失败')
            return
        } else {
            // 重命名 防止同名种子
            if (downLoadName) {
                model.torrentFile = `[${torrentInfo.hash}]${downLoadName}`
                fs.renameSync(path.join(torrentsPath, downLoadName), path.join(torrentsPath, model.torrentFile));
            }
        }

        // 新增时判断是否有重复HASH的记录
        if (!id && torrentInfo.hash && await Model.countBy({ hash_string: torrentInfo.hash })) {
            ctx.response.body = ResponseModel.failure('种子已存在')
            return
        }

        // 设置种子信息
        torrentInfo.hash && (model.hashString = torrentInfo.hash)
        torrentInfo.name && (model.btname = torrentInfo.name)
        // !model.name && (model.name = torrentInfo.name)

        if (id) {
            const updated = await Model.updateBy(model, { id: id })
            model.id = id
        } else {
            model.addtime = new Date().getTime()
            model.status = 'unlink'
            const inserted = await Model.insert(model)
            if (inserted && inserted.length > 0) {
                id = inserted[0]
                model.id = inserted[0]
            }
        }

        // 推送下载
        if (startOnAdd) {
            try {
                // hashString id name
                const torrent = await pushToClient(model, pushOptions)
                // 推送成功更新
                if (torrent) {
                    const updateModel = {
                        btid: torrent.id,
                        btname: torrent.name,
                        status: 'linked',
                    }
                    // if (!model.name) updateModel.name = torrent.name
                    await Model.updateBy(updateModel, { id: id })
                }
                // 传递给TMDB识别
                model.btname = torrent.name
            } catch (error) {
                logger.error('推送下载失败', error)
                ctx.response.body = ResponseModel.success({}, `保存成功。但未能推送[${error.message}]`)
                return
            }
        }

        // 通过解析的信息查询TMDB媒体信息
        // 还没进行过识别和无法识别时执行 没有媒体库 没有子目录
        if ((!model.libraryid || !model.toLink) && !model.tmdbid && model.btname) {
            try { 
                const res = await findMetaInfo(parseTorrentName(model.btname), null);
                if (res.length === 1) {
                    // 识别正常，直接使用
                    recognizedHandel(model, res[0]);
                    // 记录识别结果
                    // TODO intsert media_info
                } else if (res.length > 1) {
                    // 多个结果，标记需要人工选择
                    Model.updateBy({ tmdbid: -1 }, { id: id });
                } else {
                    // 识别失败，标记为无法识别
                    Model.updateBy({ tmdbid: 0 }, { id: id });
                }
            } catch (error) {
                logger.error('媒体识别失败', error)
                ctx.response.body = ResponseModel.success({}, `保存成功。但未能识别媒体信息`)
                return
            }
        }

        ctx.response.body = ResponseModel.success(id)
    },
    // 删除
    'get::/torrent/delete/:id': async function (ctx) {

        const { id } = ctx.request.params
        if (id) {
            const deleted = await Model.delete({ id: id })
        }
        ctx.response.body = ResponseModel.success()
    },
    // 更新部分属性
    'post::/torrent/update': async function (ctx) {

        const { id, siteid, categoryid, clientid, hashString, season } = ctx.request.body

        if (id) {
            const updateModel = {}
            if (siteid) {
                updateModel.siteid = siteid
            }
            if (categoryid) {
                updateModel.categoryid = categoryid
            }
            if (clientid) {
                updateModel.clientid = clientid
            }
            if (season) {
                updateModel.season = season
            }
            // 手动关联种子
            if (hashString) {
                updateModel.hashString = hashString
                updateModel.status = 'linked'
            }
            const updated = await Model.updateBy(updateModel, { id: id })
        }
        ctx.response.body = ResponseModel.success()
    },
    // 种子在下载器中的信息
    'get::/torrent/infoInClient': async function (ctx) {
        const { id } = ctx.request.query
        let data = {}
        if (id) {
            const model = await Model.getById(id)
            if (model && model.clientid && model.hashString) {
                const clientModel = await ClientModel.getById(model.clientid)
                if (clientModel) {
                    const client = BtClients.getClient(clientModel)
                    const info = await client.getTorrent(model.hashString)
                    // console.log(info)
                    if (info) {
                        const setting = await client.getSetting()
                        info.client = clientModel
                        info.baseDownlaodPath = setting.downloadDir
                        data = info
                    }
                }
            }
        }
        ctx.response.body = ResponseModel.success(data)
    },
    // 推送下载
    'post::/torrent/pushToClient': async function (ctx) {
        const { id, clientid, startNow, enableSiteFolder, enableCategoryFolder } = ctx.request.body
        let hashString = ''
        if (id) {
            const model = await Model.getById(id)
            if (model) {
                model.clientid = clientid
                const pushOptions = {
                    startNow,
                    enableSiteFolder,
                    enableCategoryFolder,
                }
                // hashString id name
                const torrent = await pushToClient(model, pushOptions)
                // 推送成功更新
                if (torrent) {
                    hashString = model.hashString
                    const updateModel = {
                        clientid: clientid,
                        btid: torrent.id,
                        btname: torrent.name,
                        status: 'linked',
                    }
                    // if (!model.name) updateModel.name = torrent.name
                    await Model.updateBy(updateModel, { id: id })
                }
            }
        }
        ctx.response.body = ResponseModel.success({ hashString })
    },
    // 手动关联到下载器
    'post::/torrent/linkToClient': async function (ctx) {
        const { id, clientid, hashString, checkInClient } = ctx.request.body
        if (id) {
            const model = await Model.getById(id)
            if (model && hashString) {
                model.clientid = clientid
                const updateModel = {
                    clientid: clientid,
                    hashString: hashString,
                    status: 'linked',
                }
                if (checkInClient) {
                    const clientModel = await ClientModel.getById(model.clientid)
                    if (clientModel) {
                        const client = BtClients.getClient(clientModel)
                        const info = await client.getTorrent(hashString)
                        // console.log(info)
                        if (info && info.hashString) {
                            updateModel.btid = info.id
                            updateModel.btname = info.name
                            // if (!model.name) updateModel.name = info.name
                        } else {
                            ctx.response.body = ResponseModel.failure("下载器中无此种子")
                            return
                        }
                    } else {
                        ctx.response.body = ResponseModel.failure("下载器不存在")
                        return
                    }
                }
                await Model.updateBy(updateModel, { id: id })
            }
        }
        ctx.response.body = ResponseModel.success({ hashString })
    },
    // 删除种子，从下载服务器删除
    'post::/torrent/remove': async function (ctx) {
        const {
            id,
            remove = true,  // 从本系统删除
            del = false,    // 从下载服务器删除
            delFile = false // 同时删除文件
        } = ctx.request.body

        if (id) {
            const model = await Model.getById(id)
            if (model) {
                // 从下载服务器删除
                if (del && model.clientid && model.hashString) {
                    const clientModel = await ClientModel.getById(model.clientid)
                    if (clientModel) {
                        const client = BtClients.getClient(clientModel)
                        await client.removeTorrent(model.hashString, delFile)
                    }
                }
                // 从本系统删除
                if (remove) {
                    const deleted = await Model.delete({ id: id })
                } else { // 仅解除与下载器的关联
                    // if (del) {
                    // 取消与下载器的关联
                    const updateModel = {
                        btid: null,
                        // hashString: null,
                        // btname: null,
                        status: 'unlink'
                    }
                    await Model.updateBy(updateModel, { id: id })
                    // }
                }
            }
        }
        ctx.response.body = ResponseModel.success()
    },
    // 调整种子在下载器中的下载位置
    'post::/torrent/setLocation': async function (ctx) {
        const { id, location, move, olocation, verify } = ctx.request.body
        if (id && location) {
            const model = await Model.getById(id)
            if (model) {
                if (model.clientid && model.hashString) {
                    const clientModel = await ClientModel.getById(model.clientid)
                    if (clientModel) {
                        const client = BtClients.getClient(clientModel)
                        const setted = await client.setTorrentLocation(model.hashString, location)
                        if (setted && verify) {
                            await client.verifyTorrent(model.hashString)
                        }
                    }
                }
            }
        }
        ctx.response.body = ResponseModel.success()
    },
    // 开始、停止、校验 种子
    'post::/torrent/doAction': async function (ctx) {
        const { id, action } = ctx.request.body
        const actions = ['start', 'stop', 'verify']
        if (id && actions.includes(action)) {
            const model = await Model.getById(id)
            if (model) {
                if (model.clientid && model.hashString) {
                    const clientModel = await ClientModel.getById(model.clientid)
                    if (clientModel) {
                        const client = BtClients.getClient(clientModel)
                        if (actions[0] === action) {
                            await client.startTorrent(model.hashString)
                        } else if (actions[1] === action) {
                            await client.stopTorrent(model.hashString)
                        } else if (actions[2] === action) {
                            await client.verifyTorrent(model.hashString)
                        }
                    }
                }
            }
        }
        ctx.response.body = ResponseModel.success()
    },
    // 链接文件准备
    'get::/torrent/linkToLibrary': async function (ctx) {
        const { id } = ctx.request.query
        let data = {}
        if (id) {
            const model = await Model.getById(id)
            if (model && model.clientid && model.hashString) {
                const clientModel = await ClientModel.getById(model.clientid)
                if (clientModel) {
                    const client = BtClients.getClient(clientModel)
                    const torrent = await client.getTorrent(model.hashString)
                    data.sourceDir = path.join(clientModel.volumeContainer, torrent.downloadDir.replace(clientModel.volumeHost, '')) // volumeContainer volumeHost
                    data.files = torrent.files ? torrent.files : []
                }
            }
        }
        ctx.response.body = ResponseModel.success(data)
    },
    // 执行链接文件
    'post::/torrent/doLinkToLibrary': async function (ctx) {
        const { torrentId, sourceDir, linkDir, linkRows, move = false } = ctx.request.body
        // console.log('torrentId', torrentId)
        // console.log('sourceDir', sourceDir)
        // console.log('linkDir', linkDir)
        // console.log('linkRows', linkRows)
        // console.log('move', move)
        if (torrentId && sourceDir && linkDir && linkRows) {

            for (let index = 0; index < linkRows.length; index++) {
                const linkRow = linkRows[index]
                if (linkRow.selected === true) {

                    // 原文件
                    const sourceFile = path.join(sourceDir, linkRow.sourceFile)
                    // 目标文件
                    const linkFile = path.join(linkDir, linkRow.linkFile)

                    console.log('%s -> %s', sourceFile, linkFile)
                    // continue

                    if (await fileExists(sourceFile)) {
                        if (await fileExists(linkFile) && !linkRow.replace) {
                            linkRow.status = 202 // 目标已存在 
                        } else {
                            fs.rmSync(linkFile, { force: true })
                            fs.mkdirSync(path.dirname(linkFile), { recursive: true })
                            if (move === true) {
                                // 移动
                                fs.renameSync(sourceFile, linkFile)
                            } else {
                                // 硬连接
                                fs.linkSync(sourceFile, linkFile)
                            }
                            linkRow.status = 1 // 成功
                        }
                    } else {
                        linkRow.status = 201 // 原文件不存在
                    }
                }
            }
            // 记录种子最近链接
            Model.updateBy({ linkDir: linkDir }, { id: torrentId })
        }
        ctx.response.body = ResponseModel.success(linkRows)
    },
    // 立即自动链接
    'get::/torrent/autoLink': async function (ctx) {
        const { dryRun } = ctx.request.query
        const { autoLinkFunc } = require('../timer/auto-link-timer')
        let data = {};
        if (dryRun === 'true') {
            // 只返回处理文件列表，不执行硬链接
            data = await autoLinkFunc(true)
        } else {
            autoLinkFunc()
        }
        ctx.response.body = ResponseModel.success(data)
    },
    // 手动识别媒体信息
    'get::/torrent/recognize': async function (ctx) {
        const { id, name, year } = ctx.request.query
        let data = []
        if (id) {
            const model = await Model.getById(id)
            if (model) {
                const query = name ? { name, year } : parseTorrentName(model.btname)
                data = await findMetaInfo(query, null);
            }
        }
        ctx.response.body = ResponseModel.success(data)
    },
    // 确认手动识别媒体信息
    'post::/torrent/recognizeConfirm': async function (ctx) {
        const info = ctx.request.body
        if (info.modelId) {
            const model = await Model.getById(info.modelId);
            await recognizedHandel(model, info)
        }
        ctx.response.body = ResponseModel.success()
    },
}
